home *** CD-ROM | disk | FTP | other *** search
- ***************************************************************************
- * How to write an external Player *
- ***************************************************************************
-
- © 1992 by Delirium
- Date 12.09.1992
-
-
-
- DeliTracker supports so called external players. These are players that
- are in a special format so that they can be loaded from DeliTracker. If
- such a player is loaded, DeliTracker recognises & plays the moduletype that
- the player supports. These external players make DeliTracker flexible. New
- players can be added by the user and old players can be updated easily. The
- user can select only the needed players. The current version DeliTracker
- can handle up to 64 external players simultaneously. This should be enough
- for the near future :)
-
- When DeliTracker is started it will load all the players in the 'DeliPlayers'
- directory (or the specified playerpath e.g. tooltypes or in the configfile).
- In addition you can add players while DeliTracker is running.
-
- External Players are executables which means they are relocated. At the start
- of an external player you can find the characteristic playerstructure. This
- structure is generated with four macros. These macros can be found in
- 'misc/deliplayer.i'.
-
- It is not difficult to adapt a player if you have the replayroutine. You only
- need to write some interfacecode. DeliTracker has some helpful builtin
- routines that will make this job a lot easier.
-
-
- 1. Basics
-
- To adapt a new soundsystem you need the replayroutine of that soundsystem, and
- at least 5 modules for testing the adaption.
-
-
- 2. Schematics of a external player
-
- {
- Playerheader
-
- TagArray
-
- Interfacecode
-
- Replaycode+Data
- }
-
-
- 3. Functions that the external players (must) have
-
- The PLAYERHEADER macro generates the header that identifies the file as a
- valid external player for DeliTracker. This macro must exist and the player
- must begin with the macro. The only parameter you must supply is a pointer
- to a Tag Array that contains all functions that the external player supports.
- In all calls to player functions (except the interrupt routine) a5 will
- contain the address to the global player datastructure (Base). For more about
- this structure read 'misc/deliplayer.i'. In your routines (except in
- DTP_Interrupt) you can trash all registers.
-
-
- PLAYERHEADER <tagarray>
-
- tagarray Pointer to a tag array, terminated with TAG_DONE.
- Tags with ti_Data NULL are ignored.
- Here is a summary of all system tags you may use:
- TAG_DONE
- TAG_IGNORE
- TAG_MORE
- TAG_SKIP
-
- DTP_CustomPlayer this tag identifies a player as customplayer.
- ti_Data may not be NULL !
-
- DTP_RequestDTVersion only if the DeliTracker version is greater than or
- equal to the requested version (ti_Data) will
- DeliTracker accept the player. If your player
- uses functions that were introduced in later revisions
- of DeliTracker you must set this tag according to the
- version that introduced this function.
-
- DTP_RequestV37 if this tag is set, only the Kick 2.0 version of
- DeliTracker will accept the player.
- (dtg_GadToolsBase is valid)
-
- DTP_PlayerVersion Tag that contains the revision number of the player.
- If there are two players with same name the player
- with the higher version is used.
-
- DTP_PlayerName ti_Data contains a pointer to the playername. This
- string may be as long as you wish, but only the first
- 24 chars are actually used. This tag must exist !
-
- DTP_Creator pointer to the author/adaptor name. This string is
- visible in the prefs window if the player is selected.
- The string may contain $A as line separator.
-
- DTP_Check1 pointer to a module identification routine. This
- routine is called after the first 1024 bytes of the
- module is loaded. If the module is shorter, the rest
- will contain zero. If the routine recognizes the
- moduleformat it must return d0=0 else d0<>0.
-
- DTP_Check2 pointer to a module identification routine. This
- routine is called after the complete module is loaded
- (and decrunched). If the routine recognizes the module
- it must return d0=0 else d0<>0.
-
- DTP_Extload pointer to a optional loadroutine for modules. If an
- error occurs d0<>0 else d0=0. Please remember to free
- all allocated resources (memory, locks,...), because
- no further player function is called.
-
- DTP_Interrupt pointer to a interruptroutine. This routine is called
- every 1/50 sec. via a timerinterrupt. Note: your
- interruptroutine is not executed in the timerinterrupt
- itself. This is the standard method for gaining the
- correct playspeed regardless of the videomode. If the
- DTP_Faster/DTP_Slower pointers are not supplied,
- DeliTracker emulates this by changing the interrupt
- frequency. If this tag doesn't exist, you must
- supply DTP_StartInt/DTP_StopInt.
-
- DTP_Stop pointer to optional stop routine. If this tag is not
- exist, DeliTracker uses the following standard method:
- stop interrupt (DTP_StopInt)
- cleanup sound (DTP_EndSnd)
- reinitialize the song (DTP_InitSnd)
- This routine will stop playing the song, reset the
- 'patterncounter' to the begin and change the playspeed
- to default. This means that the interrupt is started
- again and the song should begin to play from the
- beginning.
-
- DTP_Config pointer to an optional initialising routine. This
- routine is only called once after the player is
- loaded. Purpose:
- The player could load a playerspecific configfile.
-
- DTP_Userconfig pointer to a optional initialising routine. This
- routine is called if the user selects the 'Config'
- button in the prefswindow. Purpose: The player could
- open a playerspecific configwindow for setting
- special options (e.g instrumentpath for a sonix
- player) and saving them into a configfile.
-
- DTP_SubSongRange This tag should be supplied if the player supports
- multimodules. ti_Data points to a function that
- returns in d0 the minimum and in d1 the maximum
- subsong number.
-
- DTP_InitPlayer pointer to an initialising routine, that is called if
- a module is loaded successfully. Must return d0=0 if
- all is ok else d0<>0. The audioallocation must be done
- here. (DeliTracker has a function that does the
- allocation.) If the player supports subsongs it has
- to set dtg_SndNum(a5) to the first subsongnumber.
- If a routine for DTP_SubSongRange exists, DeliTracker
- performs this for you and you may omit the
- initialization for dtg_SndNum(a5).
-
- DTP_EndPlayer pointer to a cleanuproutine, that is called if the
- module is removed from memory. Audiochannels have to
- be freed here. (Use the DeliTracker internal
- supportroutine)
-
- DTP_InitSound pointer to an optional initialising routine. This
- routine has the task to (re)initialize the module. If
- the interrupt is started the song should begin to play
- at the beginning.
-
- DTP_EndSound pointer to an optional cleanuproutine. For example it
- can be used to reset the volumeregister or the
- audio-dma.
-
- DTP_StartInt pointer to an initialising routine, that must exist if
- DTP_Interrupt doesn't exist. It has the task to start
- the sound.
-
- DTP_StopInt pointer to a cleanuproutine, that must exist if
- DTP_Interrupt doesn't exist. It has the task to stop
- the sound.
-
- DTP_Volume pointer to function that sets the volume. This
- function is called every time the volume is changed
- (via arexx or slider) and once at the initialising
- phase of the module (before DTP_InitSnd is called).
- The mastervolume can be found in dtg_SndVol(a5). The
- mastervolume is the highest volume allowed. The
- effective volume can be calculated using the
- following formula:
- VOL_eff=((MASTERVOLUME*modulevolume)>>6).
- See also the example sources.
-
- DTP_Balance pointer to a function that sets the balance. This
- function is called every time the balance is changed
- (via arexx or slider) and once at the initialising
- phase of the module (before tf_InitSnd is called).
- The balance for the left channel can be found in
- dtg_SndLBal(a5), for the right channel in
- dtg_SndRBal(a5). Note: All players that support
- balance are capable of volume too! Then you must use
- the same routine for both operations. The mastervolume
- for the left channels can be calculated with this
- formula:
- LeftMaster =((dtg_Volume(a5)*dtg_SndLBal(a5))>>6).
- For the right channels the formula is similar.
-
- DTP_Faster pointer to a function that increases the playspeed.
-
- DTP_Slower pointer to a function that decreases the playspeed.
-
- DTP_NextPatt pointer to a function that increases the
- patternpointer.
-
- DTP_PrevPatt pointer to a function that decreases the
- patternpointer.
-
- DTP_NextSong pointer to a function that increases the
- subsongcounter (only if the subsong exists).
-
- DTP_PrevSong pointer to a function that decreases the
- subsongcounter (only if the subsong exists).
-
-
- 4. DeliTracker support functions that can be called from the external player.
-
- Every function is called like this:
-
- move.l dtg_XXX(a5),a0 ; a5 must contain the base
- jsr (a0)
-
- All functions (exept dtg_SongEnd) use d0/d1/a0/a1 as Scratchregister.
- A5 must contain the base (exept dtg_SongEnd).
-
-
- dtg_GetListData
-
- SYNOPSIS
- memory size = dtg_GetListData(number)
- a0 d0 d0.l
-
- FUNCTION
- Returns the address and the length of a file that was loaded
- with dtg_LoadFile().
-
- INPUTS
- number - number of the file beginning with 0 for the file that
- was selected by the user.
-
- RESULT
- memory - startaddress of the files in memory, if error NULL.
- size - length of the loaded file in bytes or 0 in case of
- an error
-
-
- dtg_LoadFile
-
- SYNOPSIS
- success = dtg_LoadFile(name)
-
- FUNCTION
- Loads and decrunches the specified file to chipmemory.
- Note: this function automatically adds '.pp' to the filename.
-
- INPUTS
- name - store the filename in the internal buffer
- (dtg_PathArray contains a pointer to this buffer)
-
- RESULT
- success - success d0.l=0, else d0.l<>0.
-
-
- dtg_CopyDir
-
- SYNOPSIS
- dtg_CopyDir()
-
- FUNCTION
- Copies the directory of the selected file at the end
- of the string, that dtg_PathArray points to.
-
-
- dtg_CopyFile
-
- SYNOPSIS
- dtg_CopyFile()
-
- FUNCTION
- Copies the filename of the selected file at the end
- of the string, that dtg_PathArray points to.
-
-
- dtg_CopyString
-
- SYNOPSIS
- dtg_CopyString(string)
- a0
-
- FUNCTION
- a0 contains the address of a string, which is copied at the
- end of the string that dtg_PathArray points to.
-
- INPUTS
- string - a0 contains the pointer to the string
-
-
- dtg_AudioAlloc
-
- SYNOPSIS
- success = dtg_AudioAlloc()
-
- FUNCTION
- Allocates the audiochannels
-
- RESULT
- success - if we got them: d0.l=0, else d0.l<>0.
-
-
- dtg_AudioFree
-
- SYNOPSIS
- dtg_AudioFree()
-
- FUNCTION
- Frees the audiochannels that were allocated with
- dtg_AudioAlloc.
-
-
- dtg_StartInt
-
- SYNOPSIS
- dtg_StartInt()
-
- FUNCTION
- Starts the soundinterrupt. If DTP_Interrupt exists,
- DeliTracker starts the internal timerinterrupt, else
- DTP_StartInt is called.
-
-
- dtg_StopInt
-
- SYNOPSIS
- dtg_StopInt()
-
- FUNCTION
- Stops the soundinterrupt. If DTP_Interrupt exists,
- DeliTracker stops the internal timerinterrupt, else
- DTP_StopInt is called.
-
-
- dtg_SongEnd
-
- SYNOPSIS
- dtg_SongEnd()
-
- FUNCTION
- Signals DeliTracker, that the module was played once.
- This function doesn't change any registers and is save
- to call from interrupts.
-
-
- dtg_CutSuffix
-
- SYNOPSIS
- dtg_CutSuffix()
-
- FUNCTION
- removes the suffix '.pp' at the end of the string, that
- dtg_PathArray points to.
-
-
-
- 5. Module-Recognition
-
- In order to recognize the different moduleformats every player contains a
- specific routine, that tells DeliTracker if the player can play this module or
- not. This routine has the task to check certain positions in the module that
- are identical in every module (like 'M.K.' at offset $438 in NoiseTracker
- modules) or significant assembler instructions (if the module contains the
- player). Testing against one or two branches or jumps is NOT enough, cause
- many modules with player have branchtables at the beginning of the module. If
- the player recognizes the wrong module, a system crash will be the result! So
- be careful with the Ckeckroutine. As you can see the playerstructure has two
- checkfunctions but the player must use exactly one check routine. This leads
- to two basic player types:
-
- 1.Type one Player
- Here the Check1 function is implemented.
-
- Advantage: You can implement players that can load the module itself.
- Disadvantage: No Packsupport, more complex.
-
- This type should only be used if you REALLY need to load the module by
- yourself!
- (e.g. FTM, IFF-8SVX player that loads the sample while playing, ...)
-
-
- 2. Type two Player - the easier way
- Here the Check2 function is implemented.
-
- Advantage: The module may be packed and DeliTracker handles the
- loading and allocation of the module for you.
- Disadvantage: The module is completely loaded into CHIP-Memory.
-
- Regardless of the playertype the checkfunction must return d0.l=0 if the
- player can handle this module or d0.l<>0 if not.
-
-
- 6. Player-Interrupt
-
- Players can be divided in two categories:
-
- 1. Player that uses the internal timer interrupt from DeliTracker
-
- Advantage: The player is independent from the selected videomode
- The player has automatically the faster/slower function.
- No expense for interrupthandling (interrupt structure
- and the insert/remove code).
- Compatible with the serial.device.
- Disadvantage: The interrupt is not synchronous to the VBlank (This leads
- to problems only to certain cases).
-
-
- 2. Player that generates their own interrupt.
-
- Advantage: You can use other interrupt sources (like AudioIRQ)
-
- Disadvantage: You have to handle the interrupts by yourself, and if
- you use VBlank the player is not independent to the
- videomode.
-
-
- If you use your own timerinterrupt you should allocate CIAB because CIAA
- is used from the system under 2.x. It is wise not to run the soundcode
- directly in the timerinterrupt. Instead you should Cause() a SoftInt in
- the timer interrupt and execute the routine in the SoftInt. This is to
- assure maximum compatibility for modem users because the SoftInt has a
- lower priority thant the RBF interrupt. Beware of writing directly to the
- 680x0 intvectors! You should use AddIntServer() or SetIntVector() from
- the OS.
-
-
- 7. Custom Modules
-
- These are not modules in the conventional meaning. They are more like
- external players, the difference is that custom modules contain the
- player AND the module. With the custom module interface you can adapt
- almost every module. If you have more modules with the same replay
- routine we suggest to write an own player for this moduleformat.
-
-
- Schematic of a custom module (custom player)
-
- {
- Playerheader
-
- TagArray
-
- Interfacecode
-
- Replaycode+Data
-
- MODULE
- }
-
- The Tag DTP_CustomPlayer identifies a player as a custom module.
- The following Tags are ignored for custommodules:
-
- - DTP_PlayerVersion
- - DTP_PlayerName
- - DTP_Creator
- - DTP_Check1
- - DTP_Check2
- - DTP_ExtLoad
- - DTP_Config
- - DTP_UserConfig
-
-
- 8. Checklist
-
- This is a small list that you should match when you create your own player or
- custom module.
-
- [ ] checkroutine exact enough ?
- [ ] audiochannel allocated/freed correctly ?
- [ ] all allocated memory freed after playing?
- [ ] all locks unlocked after playing ?
- [ ] enforcer and mungwall proof ?
- [ ] viable error handling path taken for ALL possible errors ?
- [ ] player tested under 1.3 and 2.0 ?
- [ ] does the player work correct in all videomodes ?
-
-
- 9. Other things
-
- The player should not change the LED condition because DeliTracker will handle
- it.
-
- Problems
-
- Symptom possible source elimination
-
- crash a5 trashed, other registers
- not saved, wrong stack usage
- initialization wrong or too late
- module too new for the replayroutine better check
-
- module sounds Audio data not in chipmem
- wrong wrong initialization
- module too new for the replayroutine
- player needs some special values
- in some registers extra init code
- wrong videomode
-
- no sound Audio-DMA off :-)
- >68000 player writes directly to processor use OS routines to
- intvectors and VBR is not 0. set IntVectors
-
- no free wrong interrupt handling VBlank-IRQ:
- CPU-time Z-Flag must be set
- at the end of the
- interruptroutine.
-
- slow system 68000/020/030 toooo slow buy a 68040 or
- CRAY ;-)
-
-
-
-
-
-